﻿using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using TiffLibrary;
using TiffLibrary.PixelBuffer;
using TiffLibrary.PixelFormats;

namespace WholeSlideImageViewer.TiffExport
{
    internal class ReducedResolutionLayerReader : ITiffPixelBufferReader<TiffRgba32>
    {
        private readonly TiffImageDecoder _lastLayer;
        private readonly TiffExportProgress _progress;

        public ReducedResolutionLayerReader(TiffImageDecoder lastLayer, TiffExportProgress progress)
        {
            _lastLayer = lastLayer;
            _progress = progress;
        }

        public int Width => (_lastLayer.Width + 1) / 2;

        public int Height => (_lastLayer.Height + 1) / 2;

        public async ValueTask ReadAsync(TiffPoint offset, TiffPixelBufferWriter<TiffRgba32> destination, CancellationToken cancellationToken)
        {
            int x = offset.X * 2;
            int y = offset.Y * 2;
            int w = destination.Width * 2;
            int h = destination.Height * 2;
            w = Math.Min(w, _lastLayer.Width - x);
            h = Math.Min(h, _lastLayer.Height - y);

            using var image = new Image<Rgba32>(w, h);
            await _lastLayer.DecodeAsync(new TiffPoint(x, y), image).ConfigureAwait(false);
            image.Mutate(ctx => ctx.Resize(destination.Width, destination.Height));
            Copy(image, destination);

            _progress.NotifyOneTileWritten();
        }

        private static void Copy(Image<Rgba32> source, TiffPixelBufferWriter<TiffRgba32> destination)
        {
            int height = source.Height;
            for (int i = 0; i < height; i++)
            {
                using TiffPixelSpanHandle<TiffRgba32> destinationHandle = destination.GetRowSpan(i);
                Span<Rgba32> sourceSpan = source.GetPixelRowSpan(i);
                MemoryMarshal.AsBytes(sourceSpan).CopyTo(MemoryMarshal.AsBytes(destinationHandle.GetSpan()));
            }
        }
    }
}
